package com.xcity.game.worldboss;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;

import org.slf4j.Logger;

import com.xcity.Server;
import com.xcity.db.entity.Player;
import com.xcity.db.entity.Ranking;
import com.xcity.game.common.MessageConst;
import com.xcity.game.event.GameEvents;
import com.xcity.game.mail.MailAttach;
import com.xcity.game.mail.MailService;
import com.xcity.game.player.cache.PlayerCacheService;
import com.xcity.game.rank.RankingList;
import com.xcity.game.rank.RankingListEnum;
import com.xcity.game.worldboss.combat.WorldBossCombatInfo;
import com.xcity.game.worldboss.model.WorldBoss;
import com.xcity.game.worldboss.template.WorldBossConfig;
import com.xcity.game.worldboss.template.WorldBossRankingRewardTemplate;
import com.xcity.game.worldboss.template.WorldBossTemplate;
import com.xcity.pomelo.PomeloRoute;
import com.xcity.pomelo.push.NoticePush;
import com.xcity.pomelo.request.WorldBossInfoRequest;
import com.xcity.pomelo.request.WorldBossJoinTimesCooldownTimeRequest;
import com.xcity.pomelo.response.WorldBossInfoResponse;
import com.xcity.pomelo.response.WorldBossInfoResponse.RankingItem;
import com.xcity.pomelo.response.WorldBossJoinTimesCooldownTimeResponse;

import naga.x.App;
import naga.x.common.Updatable;
import naga.x.event.Event;
import naga.x.event.EventListener;
import naga.x.game.template.TemplateService;
import naga.x.game.time.DayListener;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Session;
import naga.x.net.annotation.OPHandler;
import naga.x.net.impl.pomelo.annotation.PomeloOP;
import naga.x.service.Service;
import naga.x.service.ServiceException;
import naga.x.util.CollectionProcedure;

/**
 * 世界boss
 * @author yang.li
 *
 */
@OPHandler
public class WorldBossService implements Service, Updatable, EventListener, DayListener {
	
	private List<WorldBossTemplate> bossTemplates;
	private List<WorldBossRankingRewardTemplate> rankingRewardTemplates;
	
	private WorldBoss boss;
	private Queue<WorldBossCombatInfo> combatBeginQueue = new LinkedBlockingQueue<>();
	private Queue<WorldBossCombatInfo> combatEndQueue = new LinkedBlockingQueue<>();
	
	private static final Logger LOG = Server.LOG;

	@Override
	public void startup() throws ServiceException {
		bossTemplates = App.getApp().getServiceManager().get(TemplateService.class).getTemplatesList(WorldBossTemplate.class);
		rankingRewardTemplates = App.getApp().getServiceManager().get(TemplateService.class).getTemplatesList(WorldBossRankingRewardTemplate.class);
		boss = WorldBoss.deserialize();
		if (boss == null) {
			boss = new WorldBoss(bossTemplates.get(0)); // init
		}
		WorldBossUtils.service = this;
		App.getApp().getUpdater().addAsync(this);
		App.getApp().getEventManager().register(this);
		TimeUpdater.getInstance().addDayListener(this);
	}

	@Override
	public void shutdown() throws ServiceException {
		WorldBoss.serialize(boss);
	}

	@Override
	public String getId() {
		return getClass().getName();
	}
	
	@Override
	public int[] getEventTypes() {
		return new int[] { GameEvents.EVENT_WORLD_BOSS_COMBAT_BEGIN, GameEvents.EVENT_WORLD_BOSS_COMBAT_END };
	}

	@Override
	public void handleEvent(Event event) {
		switch (event.getType()) {
			case GameEvents.EVENT_WORLD_BOSS_COMBAT_BEGIN:
				combatBeginQueue.offer(event.getParameter(1));
				break;
			case GameEvents.EVENT_WORLD_BOSS_COMBAT_END:
				combatEndQueue.offer(event.getParameter(1));
				break;
		}
	}
	
	@Override
	public boolean dayChanged() {
		RankingList rl = RankingListEnum.WORLD_BOSS.instance;
		if (rl.size() == 0) {
			return true;
		}
		App.getApp().getThreadPool().execute(new RankingRewardTask(rl, rankingRewardTemplates));
		return true;
	}

	@Override
	public boolean update() {
		WorldBossCombatInfo wbci;
		while ((wbci = combatBeginQueue.poll()) != null) {
			if (wbci.bossTemplate.getLevel() == boss.getLevel()) {
				boss.addToHitList(wbci.player);
			}
		}
		while ((wbci = combatEndQueue.poll()) != null) {
			if (wbci.bossTemplate.getLevel() == boss.getLevel()) {
				boolean died = boss.decHp(wbci.npcHpDelta);
				if (died) {
					Server.broadcast(new NoticePush(NoticePush.NOTICE_MARQUEE,
							MessageFormat.format(MessageConst.WORLD_BOSS_KILLED_NOTICE, wbci.player.getName(), boss.getLevel())));
					Set<Long> hitList = boss.getHitList();
					doKillReward(boss.getTemplate(), hitList);
					int oldLvl = boss.getLevel();
					boss.fireLevelUp();
					LOG.info("[WB LVLUP]OLDLVL[{}]NEWLVL[{}]KILLER[{}]", oldLvl, boss.getLevel(), wbci.player.getId());
				}
			}
		}
		return true;
	}
	
	// 击杀奖励
	private void doKillReward(WorldBossTemplate template, Set<Long> hitList) {
		MailService ms = App.getApp().getServiceManager().get(MailService.class);
		MailAttach attach = new MailAttach();
		attach.addDrop(template.getKillDrop().getDrop());
		for (Long id : hitList) {
			ms.sendSystemMail(id,
					MessageConst.WORLD_BOSS_KILLED_REWARD_MAIL_TITLE,
					MessageFormat.format(MessageConst.WORLD_BOSS_KILLED_REWARD_MAIL_CONTENT, template.getLevel(), template.getName()),
					attach);
			LOG.info("[WB KILL REWARD]ID[{}]BOSSLVL[{}]", id, template.getLevel());
		}
	}

	public WorldBoss getBoss() {
		return boss;
	}
	
	public WorldBossTemplate getBossTemplate(int level) {
		return (level -= 1) >= 0 && level < bossTemplates.size() ? bossTemplates.get(level) : null;
	}
	
	public int getBossMaxLevel() {
		return bossTemplates.size();
	}
	
	public WorldBossRankingRewardTemplate findReward(int ranking) {
		List<WorldBossRankingRewardTemplate> list = rankingRewardTemplates;
		int start = 0, end = list.size() - 1;
		while (start <= end) {
			int mid = (start + end) >> 1;
			WorldBossRankingRewardTemplate t = list.get(mid);
			if (ranking < t.getRankingFrom()) {
				end = mid - 1;
				continue;
			}
			if (ranking > t.getRankingTo()) {
				start = mid + 1;
				continue;
			}
			return t;
		}
		return null;
	}
	
	@PomeloOP(PomeloRoute.WORLD_BOSS_INFO)
	protected void requestInfo(Session session, WorldBossInfoRequest req) {
		Player player = session.getClient();
		WorldBossInfoResponse res = new WorldBossInfoResponse(req.getSerial());
		res.level = boss.getLevel();
		res.name = boss.getTemplate().getName();
		res.icon = boss.getTemplate().getIcon();
		res.combatPower = boss.getTemplate().getCombatPower();
		res.hp = boss.getHp();
		res.maxHp = boss.getTemplate().getHp();
		res.num = WorldBossUtils.getLeftTimes(player);
		res.maxNum = WorldBossConfig.joinTimesLimit;
		res.cd = WorldBossUtils.getJoinTimesCooldownTime(player);
		res.score = WorldBossUtils.getScore(player);
		RankingList rl = RankingListEnum.WORLD_BOSS.instance;
		Ranking r = rl.findEntry(player);
		PlayerCacheService pcs = App.getApp().getServiceManager().get(PlayerCacheService.class);
		if (r != null) {
			res.ranking = r.getIndex() + 1;
			WorldBossRankingRewardTemplate reward = findReward(res.ranking);
			if (reward != null) {
				res.rewards = reward.getItemsWrapped();
			}
//			// 附近4名玩家
//			res.nearbyRankingList = new ArrayList<RankingItem>(4);
//			int offset = r.getIndex() - 3;
//			if (offset < 0) offset = 0;
//			for (int i = offset; i < offset + 4; i++) {
//				r = rl.getEntry(i);
//				if (r == null) break;
//				Player p = r.getPlayerId() == player.getId() ? player : pcs.find(r.getPlayerId());
//				res.nearbyRankingList.add(new RankingItem(p, r));
//			}
		}
		List<Ranking> rankings = rl.getEntries();
		int size = rankings.size() > 20 ? 20 : rankings.size();
		res.rankingList = new ArrayList<RankingItem>(size);
		for (int i = 0; i < size; i++) {
			r = rankings.get(i);
			Player p = pcs.find(r.getPlayerId());
			res.rankingList.add(new RankingItem(p, r));
		}
		res.rankingRewards = rankingRewardTemplates;
		session.send(res);
	}
	
	@PomeloOP(PomeloRoute.WORLD_BOSS_JOIN_TIMES_COOLDOWN_TIME)
	protected void requestJoinTimesCooldownTime(Session session, WorldBossJoinTimesCooldownTimeRequest req) {
		Player player = session.getClient();
		session.send(new WorldBossJoinTimesCooldownTimeResponse(req.getSerial(), player));
	}
	
	static class RankingRewardTask implements Runnable, CollectionProcedure<Ranking> {

		RankingList rankingList;
		List<WorldBossRankingRewardTemplate> rewardTemplates;
		
		int index;
		MailService service;
		
		RankingRewardTask(RankingList rankingList, List<WorldBossRankingRewardTemplate> rewardTemplates) {
			this.rankingList = rankingList;
			this.rewardTemplates = rewardTemplates;
			this.index = 0;
			this.service = App.getApp().getServiceManager().get(MailService.class);
		}
		
		@Override
		public void run() {
			rankingList.forEach(this);
			rankingList.clear();
		}

		@Override
		public boolean execute(Ranking ranking) {
			if (!hasReward()) {
				return false;
			}
			int n = ranking.getIndex() + 1;
			int current = index;
			WorldBossRankingRewardTemplate template = matchReward(n);
			MailAttach attach = new MailAttach();
			attach.addBatch(template.getItemsWrapped());
			service.sendSystemMail(ranking.getPlayerId(),
					MessageConst.WORLD_BOSS_RANKING_REWARD_MAIL_TITLE,
					MessageFormat.format(MessageConst.WORLD_BOSS_RANKING_REWARD_MAIL_CONTENT, n),
					attach);
			LOG.info("[WB RANKING REWARD]ID[{}]RANK[{}]REWARD[{}]", ranking.getPlayerId(), n, current);
			return true;
		}
		
		private WorldBossRankingRewardTemplate matchReward(int ranking) {
			WorldBossRankingRewardTemplate wbrrt = rewardTemplates.get(index);
			if (ranking == wbrrt.getRankingTo()) {
				index++;
			}
			return wbrrt;
		}
		
		private boolean hasReward() {
			return index < rewardTemplates.size();
		}
		
	}

}
